Posouvání objektů na plátně

python.edumach.cz

Z předchozích lekcí již víme, že příkazy můžeme psát i v okně shellu. Takto jsme postupně napsali příkazy pro import modulu tkinter a nakreslili text, obdélník, čáru a ovál. Proces v okně shellu je vidět na obrázku:

Výsledek byl stejný, kdybych tento program spustili:

#############
#  16_1.py  #
#############

import tkinter
canvas = tkinter.Canvas()
canvas.pack()
canvas.create_text(200,200,text='nadpis')
canvas.create_rectangle(100,100,150,200,fill='red')
canvas.create_line(50,50,200,150,width=5,fill='blue')
canvas.create_oval(200,100,300,150,fill='green')

Nakreslily se tyto objekty:

Podívejte se ještě jednou na okno shellu na prvním obrázku. Po nakreslení každého z obrazců se na další řádek napíše číslo vytvořeného obrazce (objektu). Všechny objekty, které vytvoříme pomocí příkazů začínajících na canvas.create_, mají po vytvoření přiřazeno číslo. Python čísluje vytvořené objekty plátna postupně od 1. Tato čísla lze použít pro různé změny objektu.

Příkazem canvas.move můžeme přesouvat objekty vytvořené na plátně. Například canvas.move(2, 30, -15) posune objekt s číslem 2 o +30 bodů na ose xx a o −15 bodů na ose yy (tj. doprava a nahoru) . V našem případě posuneme červený obdélník (má číslo 2).

>>> canvas.move(2, 30, -15)
>>>

Čísla vytvořených objektů si můžeme uložit do proměnných a pak použít proměnnou, ve které si pamatujeme číslo objektu při pohybu.


Následující program si pamatuje proměnné pro každý vytvořený objekt a po stisku šipky doprava zavolá funkci, která jednotlivé objekty posune doprava.

#############
#  16_2.py  #
#############

import tkinter
canvas = tkinter.Canvas()
canvas.pack()

nadpis = canvas.create_text(200,200,text='nadpis')
obdelnik = canvas.create_rectangle(100,100,150,200,fill='red')
cara = canvas.create_line(50,50,200,150,width=5,fill='blue')
oval = canvas.create_oval(200,100,300,150,fill='green')

def posun_vpravo(event):
    canvas.move(nadpis, 5, 0)
    canvas.move(obdelnik, 5, 0)
    canvas.move(cara, 5, 0)
    canvas.move(oval, 5, 0)

canvas.bind_all('<Right>',posun_vpravo)

canvas.mainloop()

💾 Doplň do programu možnost posouvat objekty vlevo.


Vzhledem k tomu, že vytvořené objekty jsou číslovány kontinuálně od jedničky, je stále zbytečné pamatovat si ty nakreslené v předchozím programu. Číslování lze také použít k přesunu objektů pomocí cyklu for. Předchozí program lze také napsat následovně:

#############
#  16_3.py  #
#############

import tkinter
canvas = tkinter.Canvas()
canvas.pack()

canvas.create_text(200,200,text='nadpis')
canvas.create_rectangle(100,100,150,200,fill='red')
canvas.create_line(50,50,200,150,width=5,fill='blue')
canvas.create_oval(200,100,300,150,fill='green')

def posun_vpravo(_):
    for i in range(1,5):
        canvas.move(i,5,0)

canvas.bind_all('<Right>', posun_vpravo)

canvas.mainloop()

Když přesunujeme všechny objekty najednou, můžeme místo konkrétního čísla použít zápis 'all', canvas.move('all',5,0). S tímto zápisem jsme se již setkali v příkazech canvas.delete('all'). Teď už nejspíš správně tušíme, že pokud chceme smazat jen jeden objekt, můžeme příkazem canvas.delete('all') zadat místo parametru 'all' číslo konkrétního objektu.

Následující program přesune všechny nakreslené objekty doprava a doleva jeden náhodně vybraný, jehož počet je mezi 1 a 5 (včetně).

#############
#  16_4.py  #
#############

import tkinter
import random
canvas = tkinter.Canvas()
canvas.pack()

canvas.create_text(200,200,text='nadpis')
canvas.create_rectangle(100,100,150,200,fill='red')
canvas.create_line(50,50,200,150,width=5,fill='blue')
canvas.create_oval(200,100,300,150,fill='green')

def posun_vpravo(event):
    canvas.move('all',5,0)

def posun_vlevo(event):
    canvas.move(random.randint(1,4),-5,0)

canvas.bind_all('<Right>', posun_vpravo)
canvas.bind_all('<Left>', posun_vlevo)

canvas.mainloop()

❓Otázky

  1. Jaký je rozdíl v posouvání pomocí parametru 'all' a posouváním pomocí cyklu v případě, že bychom v příkazovém řádku vytvořili nové objekty?

  2. Co se stane, když budeme posouvat nebo mazat objekt, který jsme už smazali?


Pokud jsme vytvořili obrázek z více objektů, je obtížnější procházet celý obrázek najednou. Potřebovali bychom znát číslo prvního nakresleného objektu tohoto obrázku a počet nakreslených částí. Za předpokladu, že jsme je nakreslili správně za sebou, můžeme takový obrázek posouvat ve smyčce for od čísla prvního objektu k číslu podle počtu částí. Existuje však i jednodušší možnost. Můžeme označit všechny části obrázku nějakou společnou značkou a pak můžeme procházet objekty, které tuto společnou značku mají. Objekt označíme při jeho vytváření parametrem tags, například

canvas.create_oval(x,y,x+10,y+10, tags='auto')

Při posouvání pak můžeme místo čísla tohoto objektu použít jeho značku. Například tag canvas.move('auto', 5, 0) lze použít i při mazání objektu. V následujícím programu jsme nakreslili vozík a kolo a jednotlivé části těchto obrázků jsme označili tagy. Poté je pomocí časovače přesuneme každý zvlášť.

#############
#  16_5.py  #
#############

import tkinter

canvas = tkinter.Canvas()
canvas.pack()

canvas.create_rectangle(100,150,200,200,fill='blue',tags='vozik')
canvas.create_oval(115,200,140,225,fill='yellow',tags='vozik')
canvas.create_oval(160,200,185,225,fill='yellow',tags='vozik')

canvas.create_oval(200,100,230,130, fill='',width=5, outline='black',tags='kolo')
canvas.create_oval(250,100,280,130, fill='',width=5, outline='black',tags='kolo')
canvas.create_line(215,115,230,70, width=5,tags='kolo')
canvas.create_line(225,90,240,115,265,115,270,85, width=5,tags='kolo')

def posouvej():
    canvas.move('kolo',-5,0)
    canvas.move('vozik',5,0)
    canvas.after(100,posouvej)

posouvej()

canvas.mainloop()

Kolo i vozík po určité době opustí obrazovku. Můžeme je chtít zobrazit na opačném konci. Toto lze vyřešit různými způsoby. Protože to chceme řešit pomocí příkazů, které již známe, je nejjednodušší vypočítat aktuální pozici každého obrázku a uložit do proměnné. V případě, že již bude mimo obrazovku, posuneme obrázek o celou šířku obrazovky na opačnou stranu.

#############
#  16_6.py  #
#############

import tkinter

canvas = tkinter.Canvas(bg='white', width=600, height=250)
canvas.pack()

sirka=600
x_vozik = 100
canvas.create_rectangle(100,150,200,200,fill='blue',tags='vozik')
canvas.create_oval(115,200,140,225,fill='yellow',tags='vozik')
canvas.create_oval(160,200,185,225,fill='yellow',tags='vozik')

x_kolo = 200
canvas.create_oval(200,100,230,130, fill='',width=5,outline='black',tags='kolo')
canvas.create_oval(250,100,280,130, fill='',width=5,outline='black',tags='kolo')
canvas.create_line(215,115,230,70, width=5,tags='kolo')
canvas.create_line(225,90,240,115,265,115,270,85, width=5,tags='kolo')

def posouvej():
    global x_vozik, x_kolo
    x_kolo = x_kolo-5
    canvas.move('kolo',-5,0)
    if x_kolo<0:
        x_kolo = x_kolo + sirka
        canvas.move('kolo',sirka,0)
    x_vozik = x_vozik+5
    canvas.move('vozik',5,0)
    if x_vozik>sirka:
        x_vozik = x_vozik - sirka
        canvas.move('vozik',-sirka,0)
    print("vozik:", x_vozik, "kolo:", x_kolo) #informace si muzeme vypsat i do shellu
    canvas.after(100,posouvej)

posouvej()

canvas.mainloop()